/*===========================================================================
 * Program: main.cpp
 * Author: Earl Bell
 * Date: May 30, 2011
 *
 *
 * Description:
 * 	This is the main program for the Arduino Uno micro-controller. Its function
 * is to handle the serial communication with the GUI and data acquisition from
 * the attached sensor(s).
 *===========================================================================*/




#include <WProgram.h>
#include <stdlib.h>
#include "OSEKitDefinitions.h"

//used for all sensors
uint8_t sensorMode = ENCODER_MODE;
int SENSOR_READ = 0;	//Control Variable: 1 = Read from sensor 0 = don't read from sensor


//Thermocouple
const int amplifierPin = A0;
const int temperaturePin = A2;
const int offsetPin = A1;
//We use 1024 samples because it's a power of 2 (fast division in integer math).
//Sampling 1024 times requires approximately 115ms
const int temperatureSamples = 1024;
const int temperatureWindowSize = 10; //sampling average window
uint32_t temperatureWindow[temperatureWindowSize]; //global array

//photogate
uint8_t number_of_photogates = 2;
uint8_t volatile gates_read = 0;
unsigned volatile trigger_time = 0;
uint8_t volatile triggered = 0;

//encoder
uint8_t volatile encoder_startstop = 0;



/*===========================================
 * Function:
 *
 * Description:
 *
 *
 *===========================================*/



/*===========================================
 * Function: Following code
 *
 * Description:
 * This code is to allow the use of virtual functions and other Arduino code
 *
 *===========================================*/

extern "C" void __cxa_pure_virtual()
{
  cli();
  for (;;);
}

__extension__ typedef int __guard __attribute__((mode (__DI__)));

void * operator new(size_t size)
{
  return malloc(size);
}

void operator delete(void * ptr)
{
  free(ptr);
}

void * operator new[](size_t size)
{
    return malloc(size);
}

void operator delete[](void * ptr)
{
	if (ptr)
		free(ptr);
}

int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);}
void __cxa_guard_release (__guard *g) {*(char *)g = 1;}
void __cxa_guard_abort (__guard *) {}

/*===========================================
 * Function: isSerialIncoming
 *
 * Description:
 * 	Checks the UART buffer for received serial packets.
 * Returns:
 * 	true: 1 or more bytes are waiting in the receive buffer.
 *  false: no data is in the receive buffer.
 *
 *===========================================*/
boolean isSerialIncoming()
{
	if (Serial.available() > 0)
		return true;
	else
		return false;
}

/*===========================================
 * Function: sendControlAcknowledge
 *
 * Description:
 *	Sends an ACKNOWLEDGE PACKET to the GUI through serial that
 *	is a response to the CONTROL PACKET that was sent by the GUI.
 *
 *===========================================*/
void sendAcknowledge(uint8_t status)
{
	uint8_t packet = 0x04 | 0x08 | (status << 6);
	Serial.write(packet);

}


/*===========================================
 * Function: sendMeasurementPacket
 *
 * Description:
 *	This function sends a 1 byte measurement value through the serial port as
 *	part of a measurement packet
 *
 *===========================================*/
void sendMeasurementPacket(uint32_t measurement)
{
	//write header
	//Packettype | packetlength | leftover bits
	uint8_t packet = 0x02 | 0x05 << 3 | 0 << 6;
	uint32_t mask = 0x000000FF;

	Serial.write(packet);
	for (int c = 0; c<4; c++)
	{
		Serial.write((measurement & (mask << (8*c))) >> (8*c));
	}

}


/*===========================================
 * Function: dataAcquisition
 *
 * Description:
 *	This function controls the data acquisition from the attached sensor.
 *
 *===========================================*/
void encoder_data_acquisition()
{
	sendMeasurementPacket(micros());
}


/*===========================================
 * Function: photogate_acquisition
 *
 * Description:
 *	This function controls the data acquisition for the photogates.
 *
 *===========================================*/
void photogate_data_acquisition()
{
	triggered++;
	if (triggered > 1)
	{
		detachInterrupt(0);
		//noInterrupts();

		sendMeasurementPacket(micros());
		trigger_time = millis();
		gates_read++;
		if (gates_read > number_of_photogates-1)
		{
			triggered = 0;
			trigger_time = 0;
			detachInterrupt(0);
			sendAcknowledge(ACK_DONE);
			gates_read = 0;
		}
	}


}

/*===========================================
 * Function:  setup
 *
 * Description:
 *  Initializes the Arduino Uno.
 *  -Sets serial communication speed
 *  -Sets initial temperature measurements for thermocouple
 *
 *===========================================*/
void setup ()
{
	Serial.begin(115200);
	analogReference(INTERNAL);
	//setup initial array of temperature readings
	for (int i=0; i<temperatureWindowSize; i++)
	{
		temperatureWindow[i] = analogRead(amplifierPin);
	}

	return;

}

/*===========================================
 * Function: analyzeHeader
 *
 * Description:
 * 	Reads the header byte from the received packet and returns that data.
 *
 * Returns:
 * 	int: bits 0, 1, 2: Packet type
 * 			0-2	Defines the type of packet being sent
 *			000 = NULL PACKET
 *			001 = CONFIG PACKET
 *			010 = MEASUREMENT PACKET
 *			011 = CONTROL PACKET
 *			100 = ACKNOWLEDGE PACKET
 *			101 - 111 = NULL PACKET
 *		 bits 3, 4, 5: Packet size in binary.
 *		 	Minimum 1 byte
 *		 	Max 7 bytes
 *		 bits 6, 7:
 *		   in control packet:
 *		 	00 = Stop Measurement
 *		 	11 = Start Measurement
 *		   in acknowledge packet:
 *		    00 = error
 *		    01 = done
 *		    11 = OK
 *		   in config packet:
 *		    00 = thermocouple mode
 *		    01 = encoder mode
 *		    10 = photogate mode
 *===========================================*/
int analyzeHeader()
{
	return Serial.read();
}

/*===========================================
 * Function: analyzeConfigPacket
 *
 * Description:
 *	Reads in the data bytes from a CONFIG PACKET.  Number of bytes read
 *	is equal to the packet size - 1 byte, for the header.
 *
 *===========================================*/
uint8_t analyzeConfigPacket(uint8_t packetSize)
{

    uint8_t configData[7] = {0,0,0,0,0,0,0};
    uint8_t data = 0;
    int read = 0;

	for (int packetCount = 0; packetCount < packetSize-1; packetCount++)
	{

		while (read < 1)
			read = Serial.available();

		data = Serial.read();

		if (data < 0)
		{

			return -1;
		}
		configData[packetCount] = data;
	}
	if (sensorMode == PHOTOGATE_MODE)
	{
		number_of_photogates = configData[0];
	}

	return 0;
}

/*===========================================
 * Function: encoder_startstop_tripped
 *
 * Description:
 *	Starts and stops reading from the encoder. Is triggered by the photogates
 * conntected to interrupt 0
 *
 *
 *===========================================*/

void encoder_startstop_tripped()
{
	encoder_startstop++;
	if (encoder_startstop > 1)
	{
		if (gates_read >= 1)
		{
			encoder_startstop = 0;
			detachInterrupt(0);
			detachInterrupt(1);
			trigger_time = 0;
			gates_read = 0;
			sendAcknowledge(ACK_DONE);
		}
		else
		{
			detachInterrupt(0);
			trigger_time = millis();
			attachInterrupt(1, encoder_data_acquisition, RISING); //interrupts on rising edge from the encoder
			gates_read++;
		}

	}
}


/*===========================================
 * Function: analyzeControlPacket
 *
 * Description:
 *	Reads the control value and sets or clears the SENSOR_READ control variable.
 *
 *
 *
 *===========================================*/
uint8_t analyzeControlPacket(uint8_t controlValue)
{
	if (controlValue == 0)
	//STOP READING
	{
		if (sensorMode == ENCODER_MODE)
		{
			//detachInterrupt(0); //interupts on rising edge from the photogate
			detachInterrupt(1);  //interrupts on rising edge from encoder
			SENSOR_READ = 0;
		}
		else if (sensorMode == PHOTOGATE_MODE)
		{
			detachInterrupt(0);
			gates_read = 0;
			triggered = 0;
		}
		else if (sensorMode == THERMOCOUPLE_MODE)
		{
			SENSOR_READ = 0;
		}
		else
			return -1;
	}
	else if (controlValue == 3)
	//START READING
	{
		if (sensorMode == ENCODER_MODE)
		{
			SENSOR_READ = 1;
			encoder_startstop = 0;
			trigger_time = 0;
			gates_read = 0;
			//attachInterrupt(0, encoder_startstop_tripped, RISING);  //interrupts on rising edge from photogate
			attachInterrupt(1, encoder_data_acquisition, RISING);
		}
		else if (sensorMode == PHOTOGATE_MODE)
		{
			gates_read = 0;
			triggered = 0;
			trigger_time = 0;
			attachInterrupt(0, photogate_data_acquisition, RISING);
			SENSOR_READ = 1;

		}
		else if (sensorMode == THERMOCOUPLE_MODE)
		{
			SENSOR_READ = 1;
		}
		else
			return -1;

	}
	else
		return -1;



	return 0;
}

/*===========================================
 * Function: readSerial
 *
 * Description:
 *	Reads in the incoming serial data from the GUI and takes appropriate
 *	action depending on the packet type.
 *
 *
 *===========================================*/

void readSerial()
{
	uint8_t headerData = analyzeHeader();
	uint8_t error = 0;
	uint8_t newSensorMode = 0xFF;
	if ((headerData & 0x07) == 1)
	{
		newSensorMode = (headerData & 0xC0) >> 6;
		if (newSensorMode > 0x03)
			error = -1;
		else
			sensorMode = newSensorMode;
			error = analyzeConfigPacket((headerData & 0x38) >> 3);

	}
	else if ((headerData & 0x07) == 3)
	{
		error = analyzeControlPacket((headerData & 0xC0) >> 6);
	}
	else
	{
		sendAcknowledge(ACK_ERR);
		return;
	}
	if (error != 0)
		sendAcknowledge(ACK_ERR);
	else
		sendAcknowledge(ACK_OK);

	return;
}
/*===========================================
 * Function: thermocouple_dataAcquisition
 *
 * Description:
 *	Handles data acquisition for the thermocouple sensor.
 *
 *
 *===========================================*/
void thermocouple_data_acquisition()
{
	uint32_t linearReading = 0x0011;
	uint32_t offsetReading = 0x0033;
	uint32_t thermocoupleReading = 0;
	uint32_t measurement  = 0;
	static int windowPos;

	//Read the ambient temperature and offset once (very slow drift)
	linearReading = analogRead(temperaturePin);
	offsetReading = analogRead(offsetPin);

	//Take the average of lots of amplifier samples to remove noise.
	for (int i=0; i<temperatureSamples; i++)
	{
		thermocoupleReading += analogRead(amplifierPin);
	}

	thermocoupleReading /= temperatureSamples;

	//add our latest measurement into the circular array
	if (windowPos >= temperatureWindowSize)
		windowPos = 0;
	temperatureWindow[windowPos] = thermocoupleReading;
	windowPos++;
	//take average of the array
	thermocoupleReading = 0;
	for (int i=0; i<temperatureWindowSize; i++)
	{
		thermocoupleReading += temperatureWindow[i];
	}

	thermocoupleReading /= temperatureWindowSize;

	measurement = linearReading | (thermocoupleReading <<10) | (offsetReading << 20);

	sendMeasurementPacket(measurement);
	SENSOR_READ = 0;
}


/*===========================================
 * Function: loop
 *
 * Description:
 *	This function is the main loop for the microcontroller.  This function is called in an
 *	infinite loop by the main program.
 *
 *===========================================*/

void loop ()
{

	if (isSerialIncoming())
	{
		readSerial();
	}
	if (SENSOR_READ == 1)
	{
		if (sensorMode == THERMOCOUPLE_MODE)
		{
			thermocouple_data_acquisition();
		}
		else if (sensorMode == PHOTOGATE_MODE)
		{
			if (triggered > 1)

				if ((millis() - trigger_time) > 200)
				{
					attachInterrupt(0, photogate_data_acquisition, RISING);
					trigger_time = 0;
					triggered = 0;
				}
		}
		else if (sensorMode == ENCODER_MODE)
		{
			/*if (encoder_startstop > 1)
			{
				if ((millis() - trigger_time) > 200)
				{
					attachInterrupt(0, encoder_startstop_tripped, RISING);  //interrupts on rising edge from photogate
					trigger_time = 0;
					encoder_startstop = 0;
				}

			}*/
		}
	}

}

int main(void)
{
	init();

	setup();
    
	for (;;)
		loop();
        
	return 0;
}

